
import protobuf from 'protobufjs'
import path from 'path'
import fs from 'fs-extra'
import zlib from 'zlib'
import struct from 'python-struct'

const defaultOpts = {
  path     : './pb',
  socket   : 'socket.proto',
  gmPB     : 'gm.proto',
  prefix   : 'com.autorun'
}

var options = null
var Root = null

export default class protobuffer {

  constructor (opts) {
    options = { ...defaultOpts, ...opts }
    this.ProtoBuffer = getProtoBuffer(options)
    let { socketRoot, CMsgBase, CMsgHead, GMRoot } = this.ProtoBuffer
    Root = new Map()
    Root.set('sockt', socketRoot)
    Root.set('game', GMRoot)
  }

  gameMessage = (name = 'SC_GM_QUERY') => {
    let { GMRoot } = this.ProtoBuffer
    return GMRoot.root.lookup(`${options.prefix}.game.${name}`)
  }

  makeData = buffer => {
    let zlibBuffer = compressData(buffer)
    let head = struct.pack('!i', zlibBuffer.length)
    let data = Buffer.concat([head, zlibBuffer])
    return data
  }

  decode = (buffer, CMsg) => {
    let { CMsgBase } = this.ProtoBuffer
    let ungzipBuffer = decompressData(buffer)
    let message = CMsgBase.decode(ungzipBuffer)
    if (message.msgbody) {
      let msgbody = CMsg.decode(message.msgbody)
      message.msgbody = msgbody
      return message
    }
    return ungzipBuffer
  }

  createPBBuffer = (msgtype, csMsg, params) => {
    let { CMsgBase } = this.ProtoBuffer
    let msghead = this.createHeadMessage(msgtype)
    let pbRealHead = CMsgBase.create({ msghead })
    let buffer_head = CMsgBase.encode(pbRealHead).finish()
    if (!csMsg) return buffer_head
    let { csMsgKey, socket_Root } = getCsMsgInfo(csMsg)
    let CMsgBody = socket_Root.root.lookup(`${options.prefix}.${csMsg}`)
    let pbBody = this.createBodyMessage(csMsg, params)
    let msgbody = CMsgBody.encode(pbBody).finish()
    let pbMessage = CMsgBase.create({ msghead, msgbody })
    let buffer_message = CMsgBase.encode(pbMessage).finish()
    return buffer_message
  }

  createHeadMessage = msgtype => {
    let { CMsgHead } = this.ProtoBuffer
    return CMsgHead.create({ 
      msgtype: msgtype,
      msgcode: 1
    })
  }

  createBodyMessage = (csMsg, params) => {
    let { csMsgKey, socket_Root } = getCsMsgInfo(csMsg)
    let CMsgBody = socket_Root.root.lookup(`${options.prefix}.${csMsg}`)
    return CMsgBody.create(params)
  }
}

const getProtoBuffer = (opts) => {
  let dir = opts.path
  let pbDir = path.resolve(process.cwd(), dir).replace(/([^ \/])$/, '$1/')
  let [ socketRoot, CMsgBase, CMsgHead, GMRoot ] = []
  let socketPB = path.resolve(pbDir, opts.socket)
  if (fs.existsSync(socketPB)) {
    socketRoot = protobuf.loadSync(socketPB)
    CMsgBase = socketRoot.root.lookup(`${opts.prefix}.socket.CMsgBase`)
    CMsgHead = socketRoot.root.lookup(`${opts.prefix}.socket.CMsgHead`)
  }
  let gmPB = path.resolve(pbDir, opts.gmPB)
  if (fs.existsSync(gmPB)) {
    GMRoot = protobuf.loadSync(gmPB)
  }
  return { socketRoot, CMsgBase, CMsgHead, GMRoot }
}

const compressData = buffer => 
	zlib.gzipSync(buffer, {
      windowBits: 15, 
      memLevel: 8
    })

const decompressData = buffer => 
  zlib.unzipSync(buffer, {
      windowBits: 15, 
      memLevel: 8
    })

const getCsMsgInfo = csMsg => {
  let [csMsgKey] = csMsg.split('.')
  let socket_Root = Root.get(csMsgKey) ? Root.get(csMsgKey) : socketRoot
  return { csMsgKey, socket_Root }
}
